package com.lwj.framework.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.UUID;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;

public class Utils {
	protected static final Logger logger = LoggerFactory.getLogger(Utils.class);
	public static final String LONG_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
	public static final String SHORT_DATE_FORMAT = "yyyy-MM-dd";
	private static Gson gson = getGson();

	/**
	 * 单例gson
	 *
	 * @return
	 */
	public static Gson singleGson() {
		return gson;
	}

	/**
	 * 获取Gson
	 *
	 * @return
	 */
	public static Gson getGson() {
		Gson gson = getGsonBuilder().create();
		return gson;
	}

	/**
	 * 获取GsonBuilder
	 *
	 * @return
	 */
	public static GsonBuilder getGsonBuilder() {
		return new GsonBuilder().setDateFormat(LONG_DATE_FORMAT).registerTypeAdapter(Double.class,
				numberDeserializer());
	}

	/**
	 * 数字类型解析
	 *
	 * @return
	 */
	public static JsonSerializer<Double> numberDeserializer() {
		return new JsonSerializer<Double>() {
			@Override
			public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
				if (src == src.longValue())
					return new JsonPrimitive(src.longValue());
				return new JsonPrimitive(src);
			}
		};
	}

	/**
	 * 有序的json
	 *
	 * @param obj
	 * @return
	 */
	public static String toSortJson(Object obj) {
		JsonElement tree = gson.toJsonTree(obj);
		// 排序
		sortJson(tree);
		String json = gson.toJson(tree);
		return json;
	}

	/**
	 * 获取长时间格式
	 *
	 * @return
	 */
	public static DateFormat getLongDateFormat() {
		return new SimpleDateFormat(LONG_DATE_FORMAT);
	}

	/**
	 * 获取短时间格式
	 *
	 * @return
	 */
	public static DateFormat getShortDateFormat() {
		return new SimpleDateFormat(SHORT_DATE_FORMAT);
	}

	/**
	 * date格式化
	 *
	 * @param date
	 * @param format 格式
	 * @return
	 */
	public static String formatDate(Date date, String format) {
		DateFormat dateFormat = new SimpleDateFormat(format);
		return dateFormat.format(date);
	}

	/**
	 * date长格式化
	 *
	 * @param date
	 * @return
	 */
	public static String longFormatDate(Date date) {
		return getLongDateFormat().format(date);
	}

	/**
	 * date短格式化
	 *
	 * @param date
	 * @return
	 */
	public static String shortFormatDate(Date date) {
		return getShortDateFormat().format(date);
	}

	/**
	 * 解析date
	 *
	 * @param date
	 * @param format 格式
	 * @return
	 * @throws ParseException
	 */
	public static Date parseDate(String date, String format) throws ParseException {
		DateFormat dateFormat = new SimpleDateFormat(format);
		return dateFormat.parse(date);
	}

	/**
	 * 长格式解析date
	 *
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static Date longParseDate(String date) throws ParseException {
		return getLongDateFormat().parse(date);
	}

	/**
	 * 短格式解析date
	 *
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static Date shortParseDate(String date) throws ParseException {
		return getShortDateFormat().parse(date);
	}

	/**
	 * 两个对象是否相等
	 *
	 * @param source
	 * @param target
	 * @return
	 */
	public static boolean isEquals(Object source, Object target) {
		if (source == null && target == null) {
			return true;
		}
		if (source == null || target == null) {
			return false;
		}
		return source.equals(target);
	}

	/**
	 * map转object
	 *
	 * @param map
	 * @param beanClass
	 * @return
	 * @throws Exception
	 */
	public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) throws Exception {
		if (map == null || map.isEmpty()) {
			return null;
		}
		T obj = beanClass.newInstance();
		BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor property : propertyDescriptors) {
			Method setter = property.getWriteMethod();
			if (setter != null) {
				Class<?>[] paramTypes = setter.getParameterTypes();
				if (paramTypes.length > 0) {
					Object val = map.get(property.getName());
					Class<?> paramType = paramTypes[0];
					if (val instanceof Number) {
						BigDecimal numVal = new BigDecimal(val.toString());
						if (paramType == byte.class || paramType == Byte.class) {
							Byte num = numVal.byteValue();
							setter.invoke(obj, num);
						}
						if (paramType == int.class || paramType == Integer.class) {
							Integer num = numVal.intValue();
							setter.invoke(obj, num);
						}
						if (paramType == short.class || paramType == Short.class) {
							Short num = numVal.shortValue();
							setter.invoke(obj, num);
						}
						if (paramType == double.class || paramType == Double.class) {
							Double num = numVal.doubleValue();
							setter.invoke(obj, num);
						}
						if (paramType == float.class || paramType == Float.class) {
							Float num = numVal.floatValue();
							setter.invoke(obj, num);
						}
						if (paramType == long.class || paramType == Long.class) {
							Long num = numVal.longValue();
							setter.invoke(obj, num);
						}
						if (paramType == BigDecimal.class) {
							setter.invoke(obj, numVal);
						}
					} else {
						setter.invoke(obj, val);
					}
				}
			}
		}
		return obj;
	}

	/**
	 * object转map
	 *
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> objectToMap(Object obj) throws Exception {
		Map<String, Object> map = new HashMap<String, Object>();
		BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor property : propertyDescriptors) {
			String key = property.getName();
			if (key.compareToIgnoreCase("class") == 0) {
				continue;
			}
			Method getter = property.getReadMethod();
			Object value = getter != null ? getter.invoke(obj) : null;
			map.put(key, value);
		}
		return map;
	}

	/**
	 * json转map
	 *
	 * @param json
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> jsonToMap(String json) {
		Type type = new TypeToken<Map<String, Object>>() {
		}.getType();
		return gson.fromJson(json, type);
	}

	/**
	 * map转json
	 *
	 * @param map
	 * @return
	 * @throws Exception
	 */
	public static String mapToJson(Map<String, Object> map) {
		Type type = new TypeToken<Map<String, Object>>() {
		}.getType();
		return gson.toJson(map, type);
	}

	/**
	 * 对list进行固定数量拆分
	 *
	 * @param source
	 * @param size
	 * @return
	 * @author lwj
	 */
	public static <T> List<List<T>> fixationAssign(List<T> source, int size) {
		List<List<T>> result = new ArrayList<List<T>>();
		if (size <= 0) {
			size = 50;
		}
		int quotients = source.size() / size; // 商
		int remainder = source.size() % size; // 余
		int fromIndex = 0;
		int toIndex = 0;
		for (int i = 0; i < quotients; ++i) {
			fromIndex = i * size;
			toIndex = (i + 1) * size;
			List<T> value = source.subList(fromIndex, toIndex);
			result.add(value);
		}
		if (remainder != 0) {
			List<T> value = source.subList(toIndex, source.size());
			result.add(value);
		}
		return result;
	}

	/**
	 * 将一个list均分成n个list
	 *
	 * @param source 源集合
	 * @param n      均分数量
	 * @return
	 * @author 扎克伯哥
	 * @see <a href="https://blog.csdn.net/zhaohansk/article/details/77411552">
	 *      https://blog.csdn.net/zhaohansk/article/details/77411552</a>
	 */
	public static <T> List<List<T>> averageAssign(List<T> source, int n) {
		List<List<T>> result = new ArrayList<List<T>>();
		if (n <= 0) {
			n = 5;
		}
		int quotients = source.size() / n; // 商
		int remainder = source.size() % n; // 余
		int offset = 0;// 偏移量
		for (int i = 0; i < n; i++) {
			List<T> value = null;
			if (remainder > 0) {
				value = source.subList(i * quotients + offset, (i + 1) * quotients + offset + 1);
				remainder--;
				offset++;
			} else {
				value = source.subList(i * quotients + offset, (i + 1) * quotients + offset);
			}
			result.add(value);
		}
		return result;
	}

	/**
	 * 获取被切面的类
	 *
	 * @param pjp
	 * @return
	 */
	public static Class<?> pointClass(JoinPoint pjp) {
		return pjp.getTarget().getClass();
	}

	/**
	 * 获取被切面的方法
	 *
	 * @param pjp
	 * @return
	 * @throws Exception
	 */
	public static Method pointMethod(JoinPoint pjp) throws Exception {
		Method result = null;
		Class<?> targetClass = pjp.getTarget().getClass();
		Signature signature = pjp.getSignature();
		if (signature instanceof MethodSignature) {
			MethodSignature methodSignature = (MethodSignature) signature;
			Class<?>[] paramTypes = methodSignature.getParameterTypes();
			String methodName = pjp.getSignature().getName();
			result = targetClass.getMethod(methodName, paramTypes);
		}
		return result;
	}

	/**
	 * 获取被切面类的注解
	 *
	 * @param pjp
	 * @return
	 * @throws Exception
	 */
	public static Annotation[] pointClassAnnotation(JoinPoint pjp) {
		return pjp.getTarget().getClass().getAnnotations();
	}

	/**
	 * 获取被切面方法的注解
	 *
	 * @param pjp
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	public static Annotation[] pointMethodAnnotation(JoinPoint pjp) throws Exception {
		Annotation[] result = null;
		Class<?> targetClass = pjp.getTarget().getClass();
		Signature signature = pjp.getSignature();
		if (signature instanceof MethodSignature) {
			MethodSignature methodSignature = (MethodSignature) signature;
			Class<?>[] paramTypes = methodSignature.getParameterTypes();
			String methodName = pjp.getSignature().getName();
			Method targetMethod = targetClass.getMethod(methodName, paramTypes);
			result = targetMethod.getAnnotations();
		}
		return result;
	}

	/**
	 * 是否包含{@link org.springframework.stereotype.Controller}注解
	 *
	 * @param annotations
	 * @return
	 */
	public static boolean isController(Annotation[] annotations) {
		boolean is = false;
		for (int i = 0; i < annotations.length; ++i) {
			Annotation annotation = annotations[i];
			if (annotation instanceof Controller || annotation instanceof RestController) {
				is = true;
				break;
			}
		}
		return is;
	}

	/**
	 * 是否包含{@link javax.annotation.security.PermitAll}注解
	 *
	 * @param annotations
	 * @return
	 */
	public static boolean isPermitAll(Annotation[] annotations) {
		boolean is = false;
		for (int i = 0; i < annotations.length; ++i) {
			Annotation annotation = annotations[i];
			if (annotation instanceof PermitAll) {
				is = true;
				break;
			}
		}
		return is;
	}

	/**
	 * 读取{@link javax.annotation.security.RolesAllowed} 注解的值
	 *
	 * @param handler
	 * @return
	 */
	public static Collection<String> allowedRoles(Annotation[] annotations) {
		List<String> roles = new ArrayList<>();
		if (annotations != null) {
			for (int i = 0; i < annotations.length; ++i) {
				Annotation annotation = annotations[i];
				if (annotation instanceof RolesAllowed) {
					RolesAllowed rolesAllowed = (RolesAllowed) annotation;
					roles.addAll(Arrays.asList(rolesAllowed.value()));
					break;
				}
			}
		}
		return roles;
	}

	/**
	 * 生成uuid
	 *
	 * @return
	 */
	public static String uuid() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}

	/**
	 * 字符串比较器
	 *
	 * @return
	 */
	public static Comparator<String> StringComparator() {
		Comparator<String> c = new Comparator<String>() {
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		};
		return c;
	}

	/**
	 * 根据key对json排序
	 *
	 * @param e
	 */
	public static void sortJson(JsonElement e) {
		if (e.isJsonNull()) {
			return;
		}
		if (e.isJsonPrimitive()) {
			return;
		}
		if (e.isJsonArray()) {
			JsonArray a = e.getAsJsonArray();
			for (Iterator<JsonElement> it = a.iterator(); it.hasNext();) {
				sortJson(it.next());
			}
			return;
		}

		if (e.isJsonObject()) {
			Map<String, JsonElement> tm = new TreeMap<String, JsonElement>(StringComparator());
			for (Entry<String, JsonElement> en : e.getAsJsonObject().entrySet()) {
				tm.put(en.getKey(), en.getValue());
			}
			for (Entry<String, JsonElement> en : tm.entrySet()) {
				e.getAsJsonObject().remove(en.getKey());
				e.getAsJsonObject().add(en.getKey(), en.getValue());
				sortJson(en.getValue());
			}
			return;
		}
	}

}
